home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / DCLAP 6d / dclap6d / network / ncsasock / unixlib.c < prev    next >
Text File  |  1996-07-05  |  16KB  |  619 lines

  1. /*
  2.  * BSD-style socket emulation library for the Mac
  3.  * Original author: Tom Milligan
  4.  * Current author: Charlie Reiman - creiman@ncsa.uiuc.edu
  5.  *
  6.  * This source file is placed in the public domian.
  7.  * Any resemblance to NCSA Telnet, living or dead, is purely coincidental.
  8.  *
  9.  *      National Center for Supercomputing Applications
  10.  *      152 Computing Applications Building
  11.  *      605 E. Springfield Ave.
  12.  *      Champaign, IL  61820
  13.  */
  14.  
  15. #ifdef USEDUMP
  16. # pragma load "socket.dump"
  17. #else
  18. # include <Errors.h>
  19. # include <Events.h>
  20. # include <Files.h>
  21. # include <OSUtils.h>
  22. # include <Types.h>
  23.  
  24. # include <s_types.h>
  25. # include <s_time.h>
  26.  
  27. #endif
  28.  
  29. #ifdef COMP_CODEWAR
  30. #include <s_fcntl.h>
  31. #else
  32. #include <FCntl.h>
  33. #endif
  34. #include <neti_in.h>
  35. #include <sock_ext.h>
  36. #include <sock_str.h>
  37. #ifdef THINK_C
  38. #include <pascal.h>
  39. #else
  40. #include <Strings.h>  /* for c2pstr */
  41. #endif /* THINK_C */
  42. #include <ToolUtils.h>
  43.  
  44. #include <s_timeb.h>
  45.  
  46. #ifndef _ERRNO
  47. extern int errno;
  48. #endif
  49. extern long errno_long;
  50. extern SpinFn spinroutine; 
  51.  
  52. /*
  53.  * unix getwd
  54.  *
  55.  *    where must point to 256 bytes
  56.  *    
  57.  *    work up from the current directory to the root collecting 
  58.  *    name segments as we go
  59.  */
  60.  
  61. char *getwd(char *where) 
  62. {
  63.     WDPBRec pb;
  64.     CInfoPBRec cpb;
  65.     char wdtemp[256],*start,*store,*trav;
  66.     int i;
  67.  
  68.     /* get default volume and directory last set by PBSetVol or PBHSetVol */
  69.     pb.ioNamePtr = (StringPtr) where;
  70.     PBHGetVol(&pb,false);
  71.  
  72.     /* add a colon */
  73.     (*where)++;
  74.     where[*where] = ':';
  75.     
  76.     trav = wdtemp; /* build name here */
  77.     cpb.dirInfo.ioCompletion = 0L;
  78.  
  79.     cpb.dirInfo.ioVRefNum = pb.ioWDVRefNum; /* vRefNum of volume on which */
  80.                                             /* working dir exists */
  81.     
  82.     cpb.dirInfo.ioDrDirID = pb.ioWDDirID; /* directory ID of working directory */
  83.     
  84.     while(cpb.dirInfo.ioDrDirID != 0) 
  85.     {
  86.         cpb.hFileInfo.ioNamePtr = (StringPtr) trav; /* put name segment here */
  87.         cpb.hFileInfo.ioFDirIndex = -1;
  88.         if (PBGetCatInfo(&cpb,false) != 0)  
  89.         {
  90.             cpb.dirInfo.ioDrDirID = 0;
  91.             break;
  92.         }
  93.         if (*trav == 0) 
  94.         {
  95.             cpb.dirInfo.ioDrDirID=0;
  96.             break;
  97.         }
  98.         i=*trav;      /* save length */
  99.         *trav=0;      /* null over length */
  100.         start=trav+1; /* addr of 1st char */
  101.         trav+=i+1;    /* point past current string for next name segment */ 
  102.         *trav=0;      /* initially zero length */
  103.  
  104.         cpb.dirInfo.ioDrDirID = cpb.dirInfo.ioDrParID; /* point up to parent directory */
  105.     }
  106.     
  107.     *wdtemp=0;
  108.     store=where+*where+1; /* start storing after volume name */
  109.     
  110.     if (trav-wdtemp) 
  111.         *where=(trav-wdtemp); /* set length of where as length of trav */
  112.     if (trav!=wdtemp) 
  113.         start=trav-1; 
  114.     else 
  115.         start=wdtemp;
  116.  
  117.     if (start!=wdtemp) 
  118.     {
  119.         while(*start) start--;        /* Go to beginning of string */
  120.         if (start!=wdtemp) 
  121.             start--;
  122.     }
  123.  
  124.     while(start!=wdtemp) 
  125.     {
  126.         while(*start) /* Go to beginning of string */
  127.             start--;        
  128.         trav=start+1; 
  129.         while (*trav)  /* store it */
  130.         {
  131.             *store=*trav; 
  132.             store++;
  133.             trav++; 
  134.         } 
  135.         *store=':';    /* Ready to move directory name */
  136.         store++;
  137.         if (start!=wdtemp) 
  138.             start--;
  139.         *store=0;
  140.     }    
  141.     return(p2cstr((unsigned char *) where));
  142. }
  143.  
  144. /*
  145.  * unix change working directory
  146.  */
  147. static int currentWD = 0;
  148.  
  149. #ifndef __MWERKS__
  150. /* metrowerks has same func in its unix lib */
  151. int chdir( char * pathName)
  152. {
  153.     WDPBRec pb;
  154.     char tempst[256];
  155.     long default_ioWDDirID;
  156.     short wdToClose;
  157.  
  158.     /* 
  159.      * get default volume last set by PBSetVol or PBHSetVol 
  160.      */
  161.     pb.ioNamePtr = 0L;
  162.     PBHGetVol(&pb,false);
  163.     default_ioWDDirID = pb.ioWDDirID;
  164.  
  165.     /*
  166.      * create a new mac working directory using the default volume and
  167.      * the callers partial pathname
  168.      */
  169.     strcpy(tempst,pathName);
  170.     pb.ioNamePtr = (StringPtr)c2pstr(tempst);
  171.     pb.ioVRefNum = 0;
  172.     pb.ioWDDirID = default_ioWDDirID;
  173.     pb.ioCompletion = 0L;
  174.     pb.ioWDProcID = 'UTCS';
  175.  
  176.     if ((errno = errno_long = PBOpenWD(&pb,0)) != noErr) 
  177.         return(-1);
  178.     
  179.     /*
  180.      * make the mac working directory which has just been created in 'pb'
  181.      * the new default directory. destroy the mac working directory which
  182.      * was the previous default.
  183.      */
  184. #ifdef THINK_C
  185.     if ((errno = errno_long = SetVol(0L, pb.ioVRefNum)) != noErr) 
  186. #else
  187.     if ((errno = errno_long = setvol(0L, pb.ioVRefNum)) != noErr) 
  188. #endif
  189.     {
  190.         wdToClose = pb.ioVRefNum;
  191.     } 
  192.     else 
  193.     {
  194.         wdToClose = currentWD;
  195.         currentWD = pb.ioVRefNum;
  196.     }
  197.  
  198.     /*
  199.      * close the previous working directory
  200.      */
  201.     if (wdToClose == 0)    /* nothing more to do, return */
  202.         return(0);
  203.  
  204.     pb.ioVRefNum = wdToClose;
  205.     pb.ioCompletion = 0L;
  206.     (void) PBCloseWD(&pb, false); /* ignore error */
  207.     
  208.     return(0);
  209. }
  210. #endif
  211.  
  212. /*
  213.  * Mac version of Unix system call gettimeofday. 
  214.  *
  215.  * Time is converted to the Unix epoch: Jan 1, 1970.
  216.  *
  217.  * The current timezone is always GMT.
  218.  */
  219.  
  220. static struct DateTimeRec unixEpochDtr = {1970,1,1, 0,0,0, 1};
  221.  
  222. int gettimeofday(struct timeval *tp,struct timezone *tzp) 
  223. {
  224.  
  225.     unsigned long unixEpochSecs,currentMacSecs;
  226.     
  227.     Date2Secs(&unixEpochDtr,&unixEpochSecs);
  228.     GetDateTime(¤tMacSecs);
  229.     tp->tv_sec = currentMacSecs - unixEpochSecs;
  230.     tp->tv_usec = 0;
  231.  
  232.     if (tzp != NULL)
  233.     {
  234.         tzp->tz_minuteswest = 0;    /* minutes west of Greenwich */
  235.         tzp->tz_dsttime = 0;    /* no dst correction */
  236.     }
  237.     return 0;
  238. }
  239.  
  240. #if 0
  241. /*
  242.  * Backwards compatible time call.
  243.  */
  244. time_t time(time_t *t)
  245. {
  246.     struct timeval tt;
  247.  
  248.     if (gettimeofday(&tt, (struct timezone *)0) < 0)
  249.         return (-1);
  250.     if (t)
  251.         *t = tt.tv_sec;
  252.     return (tt.tv_sec);
  253. }
  254. #endif
  255.  
  256.  
  257. /*
  258.  * The arguments are the number of minutes of time
  259.  * you are westward from Greenwich and whether DST is in effect.
  260.  * It returns a string
  261.  * giving the name of the local timezone.
  262.  *
  263.  * Sorry, I don't know all the names.
  264.  */
  265.  
  266. static struct zone {
  267.     int    offset;
  268.     char    *stdzone;
  269.     char    *dlzone;
  270. } zonetab[] = {
  271.     -1*60, "MET", "MET DST",    /* Middle European */
  272.     -2*60, "EET", "EET DST",    /* Eastern European */
  273.     4*60, "AST", "ADT",        /* Atlantic */
  274.     5*60, "EST", "EDT",        /* Eastern */
  275.     6*60, "CST", "CDT",        /* Central */
  276.     7*60, "MST", "MDT",        /* Mountain */
  277.     8*60, "PST", "PDT",        /* Pacific */
  278. #ifdef notdef
  279.     /* there's no way to distinguish this from WET */
  280.     0, "GMT", 0,            /* Greenwich */
  281. #endif
  282.     0*60, "WET", "WET DST",        /* Western European */
  283.     -10*60, "EST", "EST",        /* Aust: Eastern */
  284.     -10*60+30, "CST", "CST",    /* Aust: Central */
  285.     -8*60, "WST", 0,        /* Aust: Western */
  286.     -9*60, "JST", 0,        /* Japanese */
  287.     -1
  288. };
  289.  
  290.  
  291. char *getenv(const char *);
  292. /*char *strchr(char*, char);*/
  293.  
  294. char *timezone(long zone, short dst)
  295. {
  296.     register struct zone *zp;
  297.     static char czone[10];
  298.     char *sign;
  299.     register char *p, *q;
  300.  
  301.     if ((p = getenv("TZNAME")) != NULL) {
  302.         if ((q = strchr(p, ','))!=NULL) {
  303.             if (dst)
  304.                 return(++q);
  305.             else {
  306.                 *q = '\0';
  307.                 strncpy(czone, p, sizeof(czone)-1);
  308.                 czone[sizeof(czone)-1] = '\0';
  309.                 *q = ',';
  310.                 return (czone);
  311.             }
  312.         }
  313.         return(p);
  314.     }
  315.     for (zp=zonetab; zp->offset!=-1; zp++)
  316.         if (zp->offset==zone) {
  317.             if (dst && zp->dlzone)
  318.                 return(zp->dlzone);
  319.             if (!dst && zp->stdzone)
  320.                 return(zp->stdzone);
  321.         }
  322.     if (zone<0) {
  323.         zone = -zone;
  324.         sign = "+";
  325.     } else
  326.         sign = "-";
  327.     sprintf(czone, "GMT%s%d:%02d", sign, zone/60, zone%60);
  328.     return(czone);
  329. }
  330.  
  331. #ifdef JAE
  332. /*
  333.  * Sleep now calls the spinroutine to keep things
  334.  * moving
  335.  */
  336. int sleep(unsigned seconds) 
  337.     {
  338.     long int wakeup = TickCount() + 60*seconds;
  339.     long left;
  340.     
  341.     for (;;)
  342.         {
  343.         left = wakeup-TickCount();
  344.         
  345.         if (left <= 0)
  346.             return;
  347.             
  348.         SPIN(false,SP_SLEEP,left)
  349.         }
  350.     }
  351. #endif /* JAE */
  352.  
  353. #if 0
  354. long int getpid()
  355. {
  356.     return (42);
  357. }
  358. #endif
  359.  
  360. long int getuid()
  361. {
  362.     return (0/*root*/);
  363. }
  364.  
  365. struct passwd *getpwent()
  366. {
  367.     return (NULL/*not found*/);
  368. }
  369.  
  370. struct passwd *getpwuid()
  371. {
  372.     return (NULL/*not found*/);
  373. }
  374.  
  375. struct passwd *getpwnam()
  376. {
  377.     return (NULL/*not found*/);
  378. }
  379.  
  380. #ifdef JAE
  381. char *getlogin()
  382. {
  383.     return("macuser");
  384. }
  385. #endif /* JAE */
  386.  
  387. int chmod(char *path,int mode)
  388. {
  389. #pragma unused(path)
  390. #pragma unused(mode)
  391.     return(0);
  392. }
  393.  
  394. #if 0
  395. access(path, mode)
  396.     char *path;
  397.     int mode;
  398. {
  399. #pragma unused(path)
  400. #pragma unused(mode)
  401.     return(0);
  402. }
  403.  
  404. char *mktemp(char *atemplate)
  405. {
  406.     return(atemplate);
  407. }
  408.  
  409. void abort(void)
  410. {
  411.     exit(-1);
  412. }
  413. #endif
  414.  
  415. void bzero(void * b,long s)
  416. {
  417.     char *bc;
  418.     for(bc= (char*)b; s ; ++bc, --s)
  419.         *bc = 0;
  420. }
  421.  
  422. void bfill( void *b,long s,char fill)
  423. {
  424.     char *bc;
  425.     for(bc= (char*)b ; s ; ++bc, --s)
  426.         *bc = fill;
  427. }
  428.  
  429. void bcopy (void* c1, void* c2,long s)
  430. {
  431.     char *cc1, *cc2;
  432.     for (cc1= (char*)c1, cc2= (char*)c2 ; s ; --s)
  433.         *cc2++ = *(cc1++);
  434. }
  435.  
  436.  
  437. int bcmp (void* c1,void* c2,long s)
  438. {
  439.     char *cc1, *cc2;
  440.     for (cc1= (char*)c1, cc2= (char*)c2 ; s ; --s)
  441.         if (*cc2++ != *(cc1++)) return(1);
  442.     return(0);
  443. }
  444.  
  445. char    *sys_errlist[] = {
  446.     "Error 0",
  447.     "Not owner",                /* 1 - EPERM */
  448.     "No such file or directory",        /* 2 - ENOENT */
  449.     "No such process",            /* 3 - ESRCH */
  450.     "Interrupted system call",        /* 4 - EINTR */
  451.     "I/O error",                /* 5 - EIO */
  452.     "No such device or address",        /* 6 - ENXIO */
  453.     "Arg list too long",            /* 7 - E2BIG */
  454.     "Exec format error",            /* 8 - ENOEXEC */
  455.     "Bad file number",            /* 9 - EBADF */
  456.     "No children",                /* 10 - ECHILD */
  457.     "No more processes",            /* 11 - EAGAIN */
  458.     "Not enough core",            /* 12 - ENOMEM */
  459.     "Permission denied",            /* 13 - EACCES */
  460.     "Bad address",                /* 14 - EFAULT */
  461.     "Block device required",        /* 15 - ENOTBLK */
  462.     "Mount device busy",            /* 16 - EBUSY */
  463.     "File exists",                /* 17 - EEXIST */
  464.     "Cross-device link",            /* 18 - EXDEV */
  465.     "No such device",            /* 19 - ENODEV */
  466.     "Not a directory",            /* 20 - ENOTDIR */
  467.     "Is a directory",            /* 21 - EISDIR */
  468.     "Invalid argument",            /* 22 - EINVAL */
  469.     "File table overflow",            /* 23 - ENFILE */
  470.     "Too many open files",            /* 24 - EMFILE */
  471.     "Not a typewriter",            /* 25 - ENOTTY */
  472.     "Text file busy",            /* 26 - ETXTBSY */
  473.     "File too large",            /* 27 - EFBIG */
  474.     "No space left on device",        /* 28 - ENOSPC */
  475.     "Illegal seek",                /* 29 - ESPIPE */
  476.     "Read-only file system",        /* 30 - EROFS */
  477.     "Too many links",            /* 31 - EMLINK */
  478.     "Broken pipe",                /* 32 - EPIPE */
  479.  
  480. /* math software */
  481.     "Argument too large",            /* 33 - EDOM */
  482.     "Result too large",            /* 34 - ERANGE */
  483.  
  484. /* non-blocking and interrupt i/o */
  485.     "Operation would block",        /* 35 - EWOULDBLOCK */
  486.     "Operation now in progress",        /* 36 - EINPROGRESS */
  487.     "Operation already in progress",    /* 37 - EALREADY */
  488.  
  489. /* ipc/network software */
  490.  
  491.     /* argument errors */
  492.     "Socket operation on non-socket",    /* 38 - ENOTSOCK */
  493.     "Destination address required",        /* 39 - EDESTADDRREQ */
  494.     "Message too long",            /* 40 - EMSGSIZE */
  495.     "Protocol wrong type for socket",    /* 41 - EPROTOTYPE */
  496.     "Protocol not available",        /* 42 - ENOPROTOOPT */
  497.     "Protocol not supported",        /* 43 - EPROTONOSUPPORT */
  498.     "Socket type not supported",        /* 44 - ESOCKTNOSUPPORT */
  499.     "Operation not supported on socket",    /* 45 - EOPNOTSUPP */
  500.     "Protocol family not supported",    /* 46 - EPFNOSUPPORT */
  501.     "Address family not supported by protocol family",
  502.                         /* 47 - EAFNOSUPPORT */
  503.     "Address already in use",        /* 48 - EADDRINUSE */
  504.     "Can't assign requested address",    /* 49 - EADDRNOTAVAIL */
  505.  
  506.     /* operational errors */
  507.     "Network is down",            /* 50 - ENETDOWN */
  508.     "Network is unreachable",        /* 51 - ENETUNREACH */
  509.     "Network dropped connection on reset",    /* 52 - ENETRESET */
  510.     "Software caused connection abort",    /* 53 - ECONNABORTED */
  511.     "Connection reset by peer",        /* 54 - ECONNRESET */
  512.     "No buffer space available",        /* 55 - ENOBUFS */
  513.     "Socket is already connected",        /* 56 - EISCONN */
  514.     "Socket is not connected",        /* 57 - ENOTCONN */
  515.     "Can't send after socket shutdown",    /* 58 - ESHUTDOWN */
  516.     "Too many references: can't splice",    /* 59 - ETOOMANYREFS */
  517.     "Connection timed out",            /* 60 - ETIMEDOUT */
  518.     "Connection refused",            /* 61 - EREFUSED */
  519.     "Too many levels of symbolic links",    /* 62 - ELOOP */
  520.     "File name too long",            /* 63 - ENAMETOOLONG */
  521.     "Host is down",                /* 64 - EHOSTDOWN */
  522.     "Host is unreachable",            /* 65 - EHOSTUNREACH */
  523.     "Directory not empty",            /* 66 - ENOTEMPTY */
  524.     "Too many processes",            /* 67 - EPROCLIM */
  525.     "Too many users",            /* 68 - EUSERS */
  526.     "Disc quota exceeded",            /* 69 - EDQUOT */
  527.     "Stale NFS file handle",        /* 70 - ESTALE */
  528.     "Too many levels of remote in path",    /* 71 - EREMOTE */
  529. };
  530.  
  531. int    sys_nerr = { sizeof sys_errlist/sizeof sys_errlist[0] };
  532.  
  533. /*
  534.  * Copyright (c) 1987 Regents of the University of California.
  535.  * All rights reserved.
  536.  *
  537.  * Redistribution and use in source and binary forms are permitted
  538.  * provided that the above copyright notice and this paragraph are
  539.  * duplicated in all such forms and that any documentation,
  540.  * advertising materials, and other materials related to such
  541.  * distribution and use acknowledge that the software was developed
  542.  * by the University of California, Berkeley.  The name of the
  543.  * University may not be used to endorse or promote products derived
  544.  * from this software without specific prior written permission.
  545.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  546.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  547.  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  548.  */
  549.  
  550. #if defined(LIBC_SCCS) && !defined(lint)
  551. static char sccsid[] = "@(#)strcasecmp.c    5.6 (Berkeley) 6/27/88";
  552. #endif /* LIBC_SCCS and not lint */
  553.  
  554. #include <s_types.h>
  555.  
  556. /*
  557.  * This array is designed for mapping upper and lower case letter
  558.  * together for a case independent comparison.  The mappings are
  559.  * based upon ascii character sequences.
  560.  */
  561. static u_char charmap[] = {
  562.     '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
  563.     '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
  564.     '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
  565.     '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
  566.     '\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047',
  567.     '\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057',
  568.     '\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067',
  569.     '\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077',
  570.     '\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
  571.     '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
  572.     '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
  573.     '\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137',
  574.     '\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
  575.     '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
  576.     '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
  577.     '\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177',
  578.     '\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
  579.     '\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
  580.     '\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
  581.     '\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
  582.     '\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
  583.     '\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
  584.     '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
  585.     '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
  586.     '\300', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
  587.     '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
  588.     '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
  589.     '\370', '\371', '\372', '\333', '\334', '\335', '\336', '\337',
  590.     '\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
  591.     '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
  592.     '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
  593.     '\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
  594. };
  595.  
  596. int strcasecmp(char* s1,char* s2)
  597. {
  598.     register u_char    *cm = charmap,
  599.             *us1 = (u_char *)s1,
  600.             *us2 = (u_char *)s2;
  601.  
  602.     while (cm[*us1] == cm[*us2++])
  603.         if (*us1++ == '\0')
  604.             return(0);
  605.     return(cm[*us1] - cm[*--us2]);
  606. }
  607.  
  608. int strncasecmp(char *s1,char * s2,int n)
  609. {
  610.     register u_char    *cm = charmap,
  611.             *us1 = (u_char *)s1,
  612.             *us2 = (u_char *)s2;
  613.  
  614.     while (--n >= 0 && cm[*us1] == cm[*us2++])
  615.         if (*us1++ == '\0')
  616.             return(0);
  617.     return(n < 0 ? 0 : cm[*us1] - cm[*--us2]);
  618. }
  619.